<?php

/**
 * Description of Page
 *
 * @author Ian Clarence <ian.clarence@gmail.com>
 */
class Page extends ParentChild {

    /**
     * The table name.
     */
    const TABLE = 'page';
    
    /**
     * Forward slash, so that the ancestry appears as in the URL.
     */
    const ANCESTRY_DELIMITER = '/';

    /**
     * Page ancestry is defined in terms of the 'url' field.
     */
    const ANCESTRY_FIELD_KEY = 'url';

    /**
     * And it is not numeric, being a string.
     */
    const ANCESTRY_FIELD_KEY_NUMERIC = FALSE;

    /**
     * The panels which this page contains.
     * 
     * @var array
     */
    protected $panels = array();

    /**
     * The constructor.
     *
     * @param array $fields
     */
    public function __construct(array $fields = array()) {
        parent::__construct($fields);
        if (isset($this->id)) {
            // $this->panels = PagePanel::searchFor(self::TABLE, $this->id, 'panel');
        }
    }

    /**
     * Fetching the panels contained in the page.
     *
     * @return boolean
     */    
    private function fetchPanels() {
        $this->panels = PagePanel::searchFor(self::TABLE, $this->getId(), 'panel', TRUE);
        // return $this->validatePanels();
        return TRUE;
    }

    /**
     * Validation of the panels which this page contains.
     * Are they the right panel types?
     * 
     * @return boolean
     */    
    private function validatePanels() {
        $allowedPanelTypes = PageTypePanelType::searchFor('page_type', $this->getPageType()->getId(), 'panel_type');
        $output = TRUE;
        if (is_array($this->panels)) {
            foreach ($this->panels as $panelKey => $panel) {
                $ok = FALSE;
                $panelType = $panel->getPanelType();
                foreach ($allowedPanelTypes as $allowedPanelTypeKey => $allowedPanelType) {
                    if ($panelType == $allowedPanelType->getId()) {
                        $ok = TRUE;
                    }
                }
                $output = $output && $ok;
            }
        }
        return $output;
    }

    public function getPanels() {
        return $this->fetchPanels() ? $this->panels : FALSE;
    }

    /**
     * Getting the page object from the URL.
     *
     * @param string $url
     * @return Page
     */
    public static function getPage($url = NULL) {
        $host = Utils::getHost();
        $isSecure = Utils::getPageSecure();
        $query = new Query('
            SELECT `p`.`id`
            FROM `page` AS `p`
            INNER JOIN `subdomain` AS `s` 
            ON `s`.`id` = `p`.`subdomain`
            INNER JOIN `server_url` AS `su`
            ON `su`.`subdomain` = `s`.`id`
            AND `su`.`www` = :host
            AND `su`.`server_type` = :server
            WHERE `p`.`url` :equals :url
            -- AND `p`.`is_secure` = :isSecure
            LIMIT 1;
        ');
        $query->bindVariable('server', SERVER, FALSE);
        $query->bindVariable('host', $host);
        if ($url == NULL) {
            $query->bindTextString('equals', 'IS');
            $query->bindVariable('url', 'NULL', FALSE);
        }
        else {
            $query->bindTextString('equals', '=');
            $query->bindVariable('url', $url);
        }
        $query->bindVariable('isSecure', $isSecure ? 1 : 0, FALSE);
        $values = System::getDB()->queryArray($query, TRUE);
        if (!is_array($values)) {
            return static::pageNotFound();
        }
        if (!isset($values['id'])) {
            return static::pageNotFound();
        }
        $id = $values['id'];
        $page = new static;
        $page->load($id, 'id', TRUE);
        return $page;
    }

    /**
     * Fetching the error 404 page.
     *
     * @return Page
     */
    protected static function pageNotFound() {
        $page = new static;
        $page->load('error404', 'name', TRUE);
        apc_clear_cache('user');
        return $page;
    }

    /**
     * Provides a 'where' clause for the search method to be used in subclasses by late static binding.
     *
     * @param object $object
     * @param bool $includeWhere
     * @return string
     */
    protected static function searchWhere($object, $includeWhere = FALSE) {
        return '
            ' . ($includeWhere ? 'WHERE' : 'AND') . ' `active` = 1
            AND `is_listed` = 1
            AND `subdomain` = ' . $object->getSubdomain()->getId() . '
        ';
    }

    /**
     * Provides a suffix for APC keys relating to queries where 'searchWhere' is used.
     *
     * @param Record $object
     * @return string
     */
    protected static function apcKeySuffix($object) {
        return '_' . $object->getSubdomain()->getId();
    }

    /**
     * Returning the URL of the page.
     *
     * @param string $languageCode
     * @return string
     */
    public function getFullUrl($languageCode = NULL) {
        $protocol = $this->getIsSecure() == 1 ? 'https://' : 'http://';
        $host = ServerUrl::getHost($this->subdomain->getId());
        if (!$this->ancestry) {
            $this->fetchAncestors();
        }
        $ancestry = $this->ancestry;
        if (Registry::getVal('MULTILINGUAL_SITE') == 1) {
            $session = System::getSession();
            $language = $session->getVariable('LANGUAGE');
            $languageComponent =
                $languageCode == NULL ? (
                    ($language['code'] == Registry::getVal('DEFAULT_LANGUAGE') || $language['code'] == '') ? ''
                    : $language['code'] . '/'
                )
                : (
                    $languageCode == Registry::getVal('DEFAULT_LANGUAGE') ? ''
                    : $languageCode . '/'
                );
            return $protocol . $host . '/' . $languageComponent . $ancestry;
        }
        return $protocol . $host . '/' . $ancestry;
    }

    /**
     * Getting the Id for the current domain.
     *
     * @return int
     */
    private function getDomain() {
        return $this->getSubdomain()->getDomain()->getId();
    }

    /**
     * For a multi-domain project, getting a list of links to the homepages of all the domains.
     *
     * @return array
     */
    private static function getDomainLinks() {
        if (Registry::getVal('MULTI_DOMAIN') != 1) {
            return FALSE;
        }
        $domainLinks = array();
        foreach (Domain::getList(TRUE, TRUE, TRUE) as $domain) {
            $id = $domain['id'];
            
            $apcKey = static::TABLE . 'domains_' . $id;
            $params = array('id' => $id);
            $callback = function($params) {
                $query = new Query('
                    SELECT `p`.`id`
                    FROM `page` AS `p`
                    INNER JOIN `subdomain` AS `s`
                    ON `p`.`subdomain` = `s`.`id`
                    WHERE `p`.`url` IS NULL
                    AND `s`.`name` = :name
                    AND `s`.`domain` = :domain;
                ');
                $query->bindVariable('name', 'www');
                $query->bindVariable('domain', $params['id'], FALSE);
                return System::getDB()->queryArray($query, TRUE);
            };        
            $fields = Utils::apcCache($apcKey, $callback, STORED_AS_CACHE == YES, $params);
            
            
            $page = new Page();
            $page->load($fields['id']);
            $domainLinks[$id] = array();
            $domainLinks[$id]['id'] = $domain['id'];
            $domainLinks[$id]['name'] = $domain['name'];
            $domainLinks[$id]['full_url'] = $page->getFullUrl();
        }
        return $domainLinks;
    }

    /**
     * Links to the homepages of all the subdomains in the current domain.
     * 
     * @return array
     */
    private function getSubdomainLinks() {
        if (Registry::getVal('SUBDOMAINS') != 1) {
            return FALSE;
        }
        $subdomainLinks = array();
        foreach (Subdomain::getList(TRUE, TRUE, TRUE) as $subdomain) {
            $id = $subdomain['id'];
            
            $apcKey = static::TABLE . 'domains_' . $id;
            $params = array('id' => $id, 'domain' => $this->getDomain());
            $callback = function($params) {                
                $query = new Query('
                    SELECT `p`.`id`
                    FROM `page` AS `p`
                    INNER JOIN `subdomain` AS `s`
                    ON `p`.`subdomain` = `s`.`id`
                    WHERE `p`.`url` IS NULL
                    AND `s`.`id` = :id
                    AND `s`.`domain` = :domain;
                ');
                $query->bindVariable('id', $params['id'], FALSE);
                $query->bindVariable('domain', $params['domain'], FALSE);
                return System::getDB()->queryArray($query, TRUE);
            };        
            $fields = Utils::apcCache($apcKey, $callback, STORED_AS_CACHE == YES, $params);            
            
            $page = new Page();
            $page->load($fields['id']);
            $subdomainLinks[$id] = array();
            $subdomainLinks[$id]['id'] = $id;
            $subdomainLinks[$id]['name'] = $subdomain['name'];
            $subdomainLinks[$id]['full_url'] = $page->getFullUrl();
        }
        return $subdomainLinks;
    }

    /**
     * On a multilingual site, getting a list of links for changing the site from one language to another,
     * and displaying the current page in the new language.
     * The links are in an associative array with the language codes as indices.
     * 
     * @return array
     */
    private function getLanguageLinks() {
        if (Registry::getVal('MULTILINGUAL_SITE') != 1) {
            return FALSE;
        }
        $languageLinks = array();
        foreach (Language::getList(TRUE, TRUE, TRUE) as $language) {
            $code = $language['code'];
            $languageLinks[$code] = array();
            $languageLinks[$code]['id'] = $language['id'];
            $languageLinks[$code]['name'] = $language['name'] . ' (' . $code . ')';
            $languageLinks[$code]['full_url'] = $this->getFullUrl($code);
        }
        return $languageLinks;
    }

    /**
     * Getting page fields for passing into a view.
     * 
     * @return array
     */
    public function getFields() {

        /**
         * Original database fields.
         */
        $output = parent::getFields();

        /**
         * The full URL.
         */
        $output['full_url'] = $this->getFullUrl();

        /**
         * Domain name and subdomain: title.
         */
        $this->subdomain->loadAll();
        $this->subdomain->getDomain()->loadAll();
        $output['title_text'] = $this->subdomain->getDomain()->getDescription() . ': ' .
                $this->subdomain->getDescription() . ': ' . $this->description;

        /**
         * Location of resources.
         */
        $output['resource'] = ServerUrl::getHost($this->subdomain->getId(), 'resource');

        /**
         * Current language.
         */
        $languageCode = Utils::getLanguage();
        if ($languageCode !== FALSE) {
            $output['language'] = array();
            $language = new Language;
            $language->load($languageCode, 'code');
            $output['language']['code'] = $languageCode;
            $output['language']['id'] = $language->getId();
            $output['language']['name'] = $language->getName();
        }
        
        return $output;
    }

    /**
     * Getting page parameters for passing into a view.
     * These are the existing page fields plus extra data.
     *
     * @return array
     */
    public function getViewFields() {
        
        $viewFields['page'] = $this->getFields();

        /**
         * Languages.
         */
        $viewFields['languages'] = $this->getLanguageLinks();

        /**
         * Domains.
         */
        $viewFields['domains'] = self::getDomainLinks();

        /**
         * Subdomains.
         */
        $viewFields['subdomains'] = $this->getSubdomainLinks();
        
        $viewFields = array_merge($viewFields, parent::getViewFields());
        
        /**
         * Panels.
         */
        $panels = $this->getPanels();
        $viewFields['panels'] = Panel::getFieldsFromRecords($panels);
        foreach ($panels as $panelKey => $panel) {
            if ($panel instanceof Panel) {
                $viewFields['panels'][$panelKey] = $panel->getViewFields();
            }
        }

        /**
         * Statistical analysis.
         */
        if (SHOW_STATS == YES) {
            $stats = System::getStats();
            $stats->stopTimer('WHOLE_PAGE');
            $viewFields['stats']['db'] = $stats->getViewFields('DATABASE_CALLS');
            $viewFields['stats']['page'] = $stats->getViewFields('WHOLE_PAGE');
        }
        else {
            $viewFields['stats'] = array();
        }

        return $viewFields;
    }

    /**
     * Return an instance of the current page, from the Request URI.
     *
     * @return object
     */
    public static function getCurrentPage() {
        return RequestUri::getInstance()->getPage(get_called_class());
    }

    /**
     * When pagination is used, return the page number which is given in the URL.
     *
     * @return mixed
     */
    public static function getCurrentPageNumber() {
        return RequestUri::getInstance()->getPageNumber();
    }

    /**
     * Return the URL identifier of the current page.
     *
     * @return mixed
     */
    public static function getCurrentPageUrlIdentifier() {
        return RequestUri::getInstance()->getPageUrlIdentifier();
    }

    /**
     * When a single article is given in the URL to be viewed in the content panel,
     * return the article id number.
     *
     * @return mixed
     */
    public static function getCurrentPageArticleId() {
        return RequestUri::getInstance()->getArticleId();
    }

    /**
     * When a single article is given in the URL to be viewed in the content panel,
     * return the article parameters.
     *
     * @return array
     */
    public static function getCurrentPageArticle() {
        $articleId = RequestUri::getInstance()->getArticleId();
        if (is_numeric($articleId)) {
            return Article::getFieldsFromId($articleId);
        }
        return NULL;
    }

    /**
     * When the URL has a "pseudo-file", e.g. style.css or script.js, then return it,
     *
     * @return string
     */
    public static function getCurrentPagePseudoFile() {
        return RequestUri::getInstance()->getPseudoFile();
    }

}